package hello.aop;

import com.github.abel533.sql.SqlMapper;
import com.google.common.base.CaseFormat;
import hello.mapper.DataHisMapper;
import hello.model.DataHis;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by caixx on 2016/3/22.
 */
@Aspect //该注解标示该类为切面类
@Component //注入依赖
public class AuditAspect {
    @Autowired
    private DataHisMapper dataHisMapper;

    private ThreadLocal<List<DataHis>> dataHisTL = new ThreadLocal<>();

    @Autowired
    private SqlMapper sqlMapper;

    /**
     * 前置增强，mapper方法执行前记录审计数据
     * @param jp
     * @throws Exception
     */
    @Before("execution(* hello.mapper..update*(..))  || execution(* hello.mapper..delete*(..))")
    //annotation指定注解来截取pointcut无效
    public void beforeExecute(JoinPoint jp) throws Exception{
        Long updateBy = getCurrentUserCenterId();
        Long tenantId = getTenantId();
        MethodSignature signature = (MethodSignature) jp.getSignature();
        Method method = signature.getMethod();
        AuditData auditData = method.getAnnotation(AuditData.class);
        Object args [] = jp.getArgs();
        if (auditData != null && args.length == 1) {
            List<DataHis> dataHisList = new ArrayList<>();
            // 变更数据的ORM对象属性名
            String[] dbFieldNames = auditData.field();
            // 变更数据的数据库字段名
            Field idField = args[0].getClass().getDeclaredField("id");
            idField.setAccessible(true);
            Long id = (Long) idField.get(args[0]);
            if (null != dbFieldNames && dbFieldNames.length > 0) {
                String[] fieldNames = new String[dbFieldNames.length];
                // get old data
                Object oldData = sqlMapper.selectOne(" select * from " + auditData.table() + " where id =#{id} ", id, args[0].getClass());
                if(auditData.operate()==AuditOperateType.UPDATE) {
                    for (int i = 0; i < dbFieldNames.length; i++) {
                        fieldNames[i] = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, dbFieldNames[i]);
                        String fieldName = fieldNames[i];
                        DataHis dataHis = new DataHis();
                        dataHis.setBizId(id);
                        dataHis.setTable(auditData.table());
                        dataHis.setOperate(auditData.operate().toString());
                        // 驼峰转下划线
                        dataHis.setColumn(dbFieldNames[i]);
                        Field changeField = args[0].getClass().getDeclaredField(fieldName);
                        changeField.setAccessible(true);
                        String newData = changeField.get(args[0]).toString();
                        //获取更新前的值
                        String getMethodName = "get" + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, fieldName);
                        dataHis.setOldData(oldData.getClass().getMethod(getMethodName).invoke(oldData).toString());
                        dataHis.setNewData(newData);

                        dataHis.setUpdateTime(new Date());
                        dataHis.setUpdateBy(updateBy);
                        dataHis.setTenantId(tenantId);
                        dataHisList.add(dataHis);
                    }
                } else {
                    DataHis dataHis = new DataHis();
                    dataHis.setBizId(id);
                    dataHis.setOperate(auditData.operate().toString());
                    dataHis.setTable(auditData.table());
                    dataHis.setUpdateBy(updateBy);
                    dataHis.setTenantId(tenantId);
                    dataHis.setUpdateTime(new Date());
                    dataHisList.add(dataHis);

                }
                dataHisTL.set(dataHisList);
            }

        }


    }

    /**
     * 后置增强，根据数据更新结果选择是否记录审计数据到数据库。
     * @param jp
     * @param re
     */
    @AfterReturning(pointcut = "execution(* hello.mapper..update*(..))  || execution(* hello.mapper..delete*(..))" ,returning = "re")
    public void afterExecute(JoinPoint jp, Object re) {
        // 数据更新成功，记录审计数据
        if (re == null || ((re instanceof Long || re instanceof Integer) && Integer.parseInt(re.toString()) > 0)) {
            // 记录审计数据,可以改为异步
            for (DataHis dataHis : dataHisTL.get()) {
                dataHisMapper.addDataHis(dataHis);
            }

        } else {
            // 数据更新不成功， 清空审计数据
            dataHisTL.remove();
        }

    }

    public Long getCurrentUserCenterId() {
        String userId = String.valueOf(RequestContextHolder.currentRequestAttributes().getAttribute("user_id",
                RequestAttributes.SCOPE_REQUEST));
        return Long.valueOf(userId);
    }

    public Long getTenantId() {
        String tenantId = String.valueOf(RequestContextHolder.currentRequestAttributes().getAttribute("tenant_id",
                RequestAttributes.SCOPE_REQUEST));
        return Long.valueOf(tenantId);
    }

    public static void main(String[] args) {
        String dbFieldName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "abcDefAa");
        System.out.println(dbFieldName);
    }


}
